/******************************************************************************
* HalfCOMM_Manager.c                                                                                                                               *
*                                                                                                                                          *
* Description : half Send & Receive	Manager                                                                                                                   *
*                                                                                                                                           *
* Author : Henry                                                                                                                       *
*                                                                                                                                         *
* History : 12/13/2012 begin    :-)                                                      
*
*                                        
*                                                                                                                                       *
*                                                                                                  *                                        *
*                                                                                                                                          *
*******************************************************************************/

#define LOCAL_DEBUG_ENABLE 0

#include "global612.h"
#include "taskid.h"
#include "mpTrace.h"
#include "RS485manager.h"

#define	COMM_HALF_SEMAP	60

//pool manage.
void poolInit_Half(commPoolMan * onePool,commPoolMan * OnePoolMan,error_Pool * oneErrorPool,
				sendFunction sendFun, revFunction revFun)
{
	SemaphoreCreate(COMM_HALF_SEMAP, OS_ATTR_PRIORITY, 1);
	SemaphoreWait(COMM_HALF_SEMAP);
	
	memset(OnePoolMan,0,sizeof(OnePoolMan));
	memset(onePool,0,sizeof(onePool));
	memset(oneErrorPool,0,sizeof(oneErrorPool));
	
	OnePoolMan->sendfunFromCOMM=sendFun;
	OnePoolMan->COMMPool=onePool;
	OnePoolMan->ErrorPool=oneErrorPool;
	//OnePoolMan->revFunCOMM=revFun;
	
	SemaphoreRelease(COMM_HALF_SEMAP);
}


//pool  function 
/*
typedef struct error_Type__{
	unsigned int errorType;
	unsigned int Num; //error number.
}error_Type;

typedef struct error_Pool__ {
	unsigned int  DevType;
	unsigned int num;
	error_Type  Error[MAX_ERROR_TYPE];
	unsigned char state; // 0 invalid ,1 valid.
}error_Pool;
*/

/*
	searche free pos
*/
static unsigned int searchFreeErrPos(error_Pool * ePool, unsigned int * freeEPos)
{
	int i,ret=FAIL_POOL;
	
	for(i=0;i<MAX_ERROR_POOL;i++)
	{
		if(ePool[i].state==EP_NULL)
		{
			*freeEPos=i;
			ret=SUCC_POOL;
			break;
		}
	}
	
	return ret;
}

/*
	search pos by dev & num, get free pos by the way.
	return 1,find me, else do not find the device error.
*/
static unsigned int searchEPosByDevice(error_Pool * ePool, unsigned int * EPos,unsigned int devType,
											unsigned int devNum,unsigned int * freeEPos,unsigned int * getFree)
{
	int i,ret=FAIL_POOL;
	int getFreeFlag=0;
	
	for(i=0;i<MAX_ERROR_POOL;i++)
	{
		if((ePool[i].state==EP_BUSY) && (ePool[i].DevType==devType) && ((ePool[i].num==devNum)))
		{
			*EPos=i;
			ret=SUCC_POOL;
			break;
		}
		
		if(ePool[i].state==EP_NULL && !getFreeFlag)
		{
			*freeEPos=i;
			*getFree=1;
			getFreeFlag=1;
		}
		
	}
	
	return ret;
}


/*
	search error type, search by error type ,exist return succ else fail and get free pos.
	

*/
static unsigned int searchErrTPPos(error_Type * eType, unsigned int sEType,
									unsigned int * freeETypePos,unsigned int * getFree)
{
	int i,ret=FAIL_POOL;
	int getFreeFlag=0;
	
	for(i=0;i<MAX_ERROR_TYPE;i++)
	{
		if(eType[i].errorType==0 && !getFreeFlag)
		{
			*freeETypePos=i;
			*getFree=1;
		}

		if(eType[i].errorType==sEType)
		{
			ret=SUCC_POOL;
			*freeETypePos=i;
			break;
		}
	}
	
	return ret;
}

/*
	add one error into pool 
*/
static void addErrorToPool(error_Pool * errorPool,unsigned int devType,
		unsigned int devNum,unsigned int errorType)
{
	unsigned int errorPos=0;
	unsigned int freePos=0;
	unsigned int getFreeFlag=0;
	unsigned int ret=0;
	error_Type * eType;
	unsigned int errorTypePos=0;
	unsigned int getFreeTypePosF=0;
	
	ret=searchEPosByDevice(errorPool,&errorPos,devType,devNum,&freePos,&getFreeFlag);
	if(ret==SUCC_POOL) //find the device&number have benn added.
	{
		eType=(errorPool +errorPos)->Error;
		//here I am />_<\. 2012/12/25. Christmas
		ret=searchErrTPPos(eType,errorType,&errorTypePos,&getFreeTypePosF);
		if(ret==SUCC_POOL) //the error type exist 
		{
			eType[errorTypePos].Num++;			
		}
		else
		{
			//add error type .
			eType[errorTypePos].errorType=errorType;
			eType[errorTypePos].Num++;
		}
	}
	else
	{
		if(getFreeFlag) //find a free postion from error pool.
		{
			errorPool[freePos].DevType=devType;
			errorPool[freePos].num=devNum;
			errorPool[freePos].state=EP_BUSY;
			eType=(errorPool +freePos)->Error;
			eType[0].errorType=errorType;
			eType[0].Num++;				
		}
			
	}
}

void defaltErrorHandle(commPoolMan * poolMan, unsigned int errorType)
{	
	if(poolMan->ErrorPool==NULL) return ;
	unsigned int currPos=poolMan->currPos;
	unsigned int currDevType=poolMan->COMMPool[currPos].SendType;
	unsigned int currDevNum=poolMan->COMMPool[currPos].SendNumber;
	
	switch(errorType)
	{
		case ERROR_TO_TRYOVER:
		case ERROR_GET_SHORT:
		case ERROR_CHECK:
			addErrorToPool(poolMan->ErrorPool,currDevType,currDevNum,errorType);
			break;
			
		case ERROR_UNKNOW:
			break;
				
		default:
		break;
	}
	
			
}

static unsigned int searchER3(error_Pool * errorPool,unsigned int devType,
		unsigned int devNum,unsigned int errorType)
{
	unsigned int errorPos=0;
	unsigned int freePos=0;
	unsigned int getFreeFlag=0;
	unsigned int ret=0;
	error_Type * eType;
	unsigned int errorTypePos=0;
	unsigned int getFreeTypePosF=0;
	unsigned int errorCun=0;
	
	ret=searchEPosByDevice(errorPool,&errorPos,devType,devNum,&freePos,&getFreeFlag);
	if(ret==SUCC_POOL) //find the device&number have benn added.
	{
		eType=(errorPool +errorPos)->Error;
		ret=searchErrTPPos(eType,errorType,&errorTypePos,&getFreeTypePosF);
		if(ret==SUCC_POOL) //the error type exist 
		{
			errorCun=eType[errorTypePos].Num;	
		}
	}
	
	return errorCun;
}

void clearOneDevError(commPoolMan * poolMan, unsigned int devType,unsigned int devNum)
{
	unsigned int errorPos=0;
	unsigned int freePos=0;
	unsigned int getFreeFlag=0;
	unsigned int ret=0;
	
	ret=searchEPosByDevice(poolMan->ErrorPool,&errorPos,devType,devNum,&freePos,&getFreeFlag);
	if(ret==SUCC_POOL) //find the device&number have benn added.
	{
		memset(poolMan->ErrorPool,0,sizeof(error_Pool));		
	}
}

/*
	search condition:
	1 ,device type 
	2 ,device type & number. 
	3, device type & number & error type.
	4, device type & error type.

	return the error type.

#define ER_SC_DEVTP 			1
#define ER_SC_DEVTP_NUM 		2
#define ER_SC_DEVTP_NUM_ETP	3
#define ER_SC_DEVTP_ETP		4
	
*/
unsigned int countError(commPoolMan * poolMan,unsigned int searchWay, unsigned int devType,unsigned int devNum,unsigned int eType)
{
	unsigned ret=0;
	switch(searchWay)
	{
		case ER_SC_DEVTP:
			
			break;
		case ER_SC_DEVTP_NUM:
			
			break;
			
		case ER_SC_DEVTP_NUM_ETP:
			searchER3(poolMan->ErrorPool,devType,devNum,eType);
			break;
			
		case ER_SC_DEVTP_ETP:
			
			break;
			
		default:
			break;
	}
	
	return ret;
}

/*
	oneNode: 
	return the node state .

	char dataBuffer[COMM_BUF_LEN_MAX];
	unsigned int bufferLen;
	unsigned int timeOutLimit;
	unsigned char State; //0, invalid, 1,add , 2, wait Send
	unsigned char sendPriority;
	unsigned int SendType; //send device type
	unsigned int SendNumber; //send device number. 	
	unsigned int addNodeTime; //current time.
	unsigned int sendTimes;//have been sent times
	void (*ErrorHandle)(struct comm_pool_ * node, unsigned char errorType); //error handle function.	
*/

unsigned int  nodeHandle(commPoolMan * poolMan,comm_pool * oneNode)
{
	if(oneNode->State== NODE_INVALID)
		return NODE_INVALID; //the node is null .

	//time out handle.
	if(GET_ELAPSED(oneNode->addNodeTime) > oneNode->timeOutLimit) //time out
	{
		if(oneNode->State==NODE_WAIT) //wait for get.
		{
			if(oneNode->sendTimes>DEF_TRY_TIMES) // times over.
			{
				oneNode->State= NODE_INVALID; //invalid  node.
				if(poolMan->validNodeNum>0)
					poolMan->validNodeNum--;
				//error handle .
				return NODE_INVALID;
			}
			else
			{
				return NODE_WAIT_NEEDSEND;
			}
			return NODE_WAIT;
		}
		
		if(oneNode->State==NODE_ADD)
		{
			if(GET_ELAPSED(oneNode->addNodeTime) > ADD_AND_TO)
			{
				oneNode->State= NODE_INVALID; //invalid  node. delete it.
				if(poolMan->validNodeNum>0)
					poolMan->validNodeNum--;
				//error handle 
				return NODE_INVALID;
			}
			else
			{
				return NODE_ADD;
			}
		}
		
	}

	return oneNode->State;
	
}

/*
	check the pool ,check timer out ,try times out.
*/
void searchPool(commPoolMan * poolMan)
{
	int i=0;
	unsigned int retState=0;
	unsigned int MAXPriority=PRIORITY_LOW_COMM;
	unsigned int MAXPriorityPos=0;
	int waitFlag=0;
	int addFlag=0;
	//SemaphoreWait(COMM_HALF_SEMAP);
	for(i=0;i<sizeof(poolMan->COMMPool)/sizeof(comm_pool);i++)
	{
		retState=nodeHandle(poolMan,poolMan->COMMPool + i);
		switch(retState)
		{
			case NODE_ADD:
				if(MAXPriority > poolMan->COMMPool[i].sendPriority)
				{
					MAXPriority=poolMan->COMMPool[i].sendPriority;					
				}
				
				MAXPriorityPos=i;
				addFlag=1; //find add.
				
				break;
				
			case NODE_INVALID:
				//not anything to do .
				break;
				
			//try again,exit 
			case NODE_WAIT_NEEDSEND: 		
				poolMan->currPos=i;
				poolMan->PoolDo=POOL_SEND;
				
				//SemaphoreRelease(COMM_HALF_SEMAP);
				return ; 
				
				break;
				
			case NODE_WAIT:
				waitFlag=1;
				break;
				
			default:
				break;
		}
	}

	if(waitFlag==1) //find someone wait for get ...
	{
		// wait,nothing to do .
		poolMan->PoolDo=POOL_WAIT;
		//SemaphoreRelease(COMM_HALF_SEMAP);
		return ;
	}
	//else
	
	if(addFlag==1) //find someone add & no one wait ,so,send it .
	{
		poolMan->PoolDo=POOL_SEND;
		poolMan->currPos=MAXPriorityPos;
		//SemaphoreRelease(COMM_HALF_SEMAP);
		return ;
	}
	
	//else //I am free now , ^_^ please tell me what to do now ..... 
	{
		poolMan->PoolDo=POOL_FREE;
		poolMan->currPos=0;	
		//SemaphoreRelease(COMM_HALF_SEMAP);
		return ;
	}
	
}

//get first free pos from pool
unsigned int getFreePoolPos(commPoolMan * poolMan, unsigned int *  getPos)
{
	int i=0;
	unsigned int retState=0;
	unsigned char getFlag=FAIL_POOL;
	
	for(i=0;i<sizeof(poolMan->COMMPool)/sizeof(comm_pool);i++)
	{
		retState=nodeHandle(poolMan,poolMan->COMMPool + i);
		if(retState==NODE_INVALID)
		{
			getFlag=1;
			break;
		}		
	}

	* getPos=i;
	
	return getFlag;	
}

/*
	return 1 succ ,else fail .
*/
unsigned char  addNodeToPool(commPoolMan * poolMan,char * sendBuf,unsigned int bufLen,
						unsigned char Priority, unsigned int Type,unsigned int number,unsigned int TO,
						ErrorHandle *errorHandle)
{
	unsigned int addPos=0;
	SemaphoreWait(COMM_HALF_SEMAP);
	//buffer over.
	if(bufLen>COMM_BUF_LEN_MAX) 
	{
		SemaphoreRelease(COMM_HALF_SEMAP);
		return FAIL_POOL;
	}
	
	if(getFreePoolPos(poolMan,&addPos) ==SUCC_POOL)
	{
		memset((char *)(poolMan->COMMPool + addPos),0,sizeof(comm_pool));
		memcpy(poolMan->COMMPool[addPos].dataBuffer,sendBuf,bufLen);
		poolMan->COMMPool[addPos].bufferLen=bufLen;
		poolMan->COMMPool[addPos].sendPriority=Priority;
		poolMan->COMMPool[addPos].SendType=Type;
		poolMan->COMMPool[addPos].SendNumber=number;
		if(TO!=0)			
			poolMan->COMMPool[addPos].timeOutLimit=TO;
		else
			poolMan->COMMPool[addPos].timeOutLimit=DEF_TIMEOUT;
		//poolMan->COMMPool[addPos].sendTimes=0;
		poolMan->COMMPool[addPos].addNodeTime=GETSYSTM;
		poolMan->COMMPool[addPos].State=NODE_ADD;
		
		if(errorHandle!=NULL)
			poolMan->COMMPool[addPos].errorHandle=errorHandle;
		else
			poolMan->COMMPool[addPos].errorHandle=defaltErrorHandle;
		
		poolMan->validNodeNum++;
		
		SemaphoreRelease(COMM_HALF_SEMAP);
		return SUCC_POOL;
	}

	SemaphoreRelease(COMM_HALF_SEMAP);
	
	return FAIL_POOL;

	
}

/*
	return 1,send one node,else ,not do anything.
*/
unsigned char sendNodeFromPool(commPoolMan * poolMan)
{
	//SemaphoreWait(COMM_HALF_SEMAP);
	unsigned int pos=poolMan->currPos;
	char * sendBuff=poolMan->COMMPool[pos].dataBuffer;
	unsigned int bufLen=poolMan->COMMPool[pos].bufferLen;
	comm_pool * oneNode=poolMan->COMMPool + pos;
	
	if(poolMan->PoolDo!=POOL_SEND)
	{
		return FAIL_POOL; //not anything to do .
	}
	
	if(poolMan->currPos>COMM_LIST_LEN_MAX) 
	{
		return FAIL_POOL; //position over 
	}

	
	poolMan->sendfunFromCOMM(sendBuff,bufLen);
	
	oneNode->sendTimes++;
	oneNode->State=NODE_WAIT;	
	poolMan->PoolDo=POOL_WAIT;
	
	return SUCC_POOL;
}

void deleteOneNodeFromPool(commPoolMan * poolMan,unsigned int pos)
{
	if(pos>COMM_LIST_LEN_MAX)  //pool over.
	{
		return ;
	}

	memset((char *)(poolMan->COMMPool + pos),0,sizeof(comm_pool));
	if(poolMan->validNodeNum>0)
		poolMan->validNodeNum--;
	poolMan->PoolDo=POOL_UNKNOW;
	
}

void poolRun(commPoolMan * poolMan)
{
	unsigned int errorNum;
	SemaphoreWait(COMM_HALF_SEMAP);
	
	unsigned int pos=poolMan->currPos;
	comm_pool * currNode=poolMan->COMMPool + pos; 
	
	if(poolMan->revFunCOMM!=NULL)
	{
		poolMan->revFunCOMM(&errorNum);

		if(errorNum==ERROR_NO) //success rev and not any error.communication end.
		{
			deleteOneNodeFromPool(poolMan,poolMan->currPos);
			poolMan->revSuccCun++;
			if(poolMan->revSuccCun==0xffffffff)
			{
				poolMan->revSuccCun=0;
				poolMan->high32Rev++;
			}
		}
		else
		{
			if(poolMan->COMMPool[pos].errorHandle!=NULL)
			{
				poolMan->COMMPool[pos].errorHandle(poolMan,errorNum);
			}
		}
	}
	
	searchPool(poolMan);
	if(sendNodeFromPool(poolMan)==SUCC_POOL)
	{
		poolMan->sendSuccCun++;
		if(poolMan->sendSuccCun==0xffffffff)
		{
			poolMan->sendSuccCun=0;
			poolMan->high32Send++;
		}
	}
	
	SemaphoreRelease(COMM_HALF_SEMAP);
	
}
//pool  function end.
/*
	---  |\ | |****
	|--  | \| *     *
	---         L**** Henry ^ /_\ ^
*/

//demo and how to use the pool 
comm_pool CommPool[COMM_LIST_LEN_MAX];
error_Pool errorPool[MAX_ERROR_POOL];
commPoolMan	PoolMan;

void commHandle()
{
	return ;
}

void revFunDemo(unsigned int *errorNum) //user function for how to rev and handle.
{
	//commHandle();
	return 	ERROR_NO;
	//return other error from user.
}

void sendFunDemo(char * buff,unsigned int bufLen)
{
	return ;
}

/*
	It can be done as tast or timer,please ...
	You must malloc\apply the communication pool ,error pool can tell you what error happen,but it is not necessary.
	You can use countError,etc function to get one device error and communication state from pool. 
	
*/
void poolDemo()
{
	poolInit_Half(CommPool,&PoolMan,errorPool,sendFunDemo,revFunDemo);

	while(1)
	{
		poolRun(&PoolMan);
	}
}


